using System;
using System.Collections.Generic;
using System.Text;
using Evolution.Engine.Managers;
using Microsoft.Xna.Framework;
using Evolution.Engine.Objects;

namespace Evolution.Engine.Physics
{
    public class PhysicsManager : Singleton<PhysicsManager>, Evolution.Engine.Interfaces.IUpdateable
    {
        public void Update(GameTime gameTime)
        {
            /*
                        // Only allow sources to fix position if collision detection is enabled
                        if (m_bCollision && m_nUpdatePlayerCount > 0)
                        {
                            // Allow all our registered callbacks to update the player position
                            for (i = 0; i < m_nUpdatePlayerCount; i++)
                            {
                                UPDATEPLAYER UpdatePlayer = (UPDATEPLAYER)m_pUpdatePlayer[i].pFunction;
                                if (UpdatePlayer(m_pUpdatePlayer[i].pContext, this, TimeScale)) bUpdated = true;

                            } // Next Player Callback

                        } // End if collision enabled

                        if (!bUpdated)
                        {
                            // Just move
                            Move(m_vecVelocity * TimeScale);

                        } // End if collision disabled

                        // Let our camera update if required
                        m_pCamera->Update(TimeScale, m_fCameraLag);

                        // Only allow sources to fix position if collision detection is enabled
                        if (m_bCollision && m_nUpdateCameraCount > 0)
                        {
                            // Allow all our registered callbacks to update the camera position
                            for (i = 0; i < m_nUpdateCameraCount; i++)
                            {
                                UPDATECAMERA UpdateCamera = (UPDATECAMERA)m_pUpdateCamera[i].pFunction;
                                UpdateCamera(m_pUpdateCamera[i].pContext, m_pCamera, TimeScale);

                            } // Next Camera Callback

                        } // End if collision enabled.

                        // If we've been off the ground for more than 200 milliseconds
                        // then lower traction (We leave them with a little bit of control, 
                        // i.e. if they flap their arms hard enough ;)
                        if (!GetOnFloor())
                        {
                            SetTraction(0.1f);
                            SetSurfaceFriction(0.0f);

                        } // End if not on floor
                        else
                        {
                            SetTraction(1.0f);
                            SetSurfaceFriction(10.0f);

                        } // End if on floor

                        // Increment timer
                        m_fOffFloorTime += TimeScale;
             */
        }
    }

    public class PhysicsObject
    {
        private float _Friction = 0.01f;
        private float _Mass = 0;
        private Vector3 _Gravity = new Vector3(0, -400f, 0);
        private Vector3 _Force = Vector3.Zero;
        private float _AirFriction = 0.01f;
        private float _Traction = 1.0f;
        private Vector3 _Velocity = Vector3.Zero;
        private BaseObject _ConnectedObject;

        public BaseObject ConnectedObject { get { return _ConnectedObject; } set { _ConnectedObject = value; } }
        public Vector3 Velocity { get { return _Velocity; } set { _Velocity = value; } }
        public float Traction { get { return _Traction; } set { _Traction = value; } }
        public float AirFriction { get { return _AirFriction; } set { _AirFriction = value; } }
        public Vector3 Force { get { return _Force; } set { _Force = value; } }
        public Vector3 Gravity { get { return _Gravity; } set { _Gravity = value; } }
        public float Mass { get { return _Mass; } set { _Mass = value; } }
        public float Friction { get { return _Friction; } set { _Friction = value; } }

        public PhysicsObject(BaseObject Connected)
        {
            _ConnectedObject = Connected;
            SetDefaults();
        }

        private void SetDefaults()
        {
            _Gravity = EnvironmentManager.Instance.Gravity;
            _Friction = EnvironmentManager.Instance.Friction;
            _AirFriction = EnvironmentManager.Instance.AirFriction;
            _Traction = EnvironmentManager.Instance.Traction;
        }

        public void ApplyForce(Vector3 force)
        {
            _Force = force;
        }

        public void Update(GameTime gameTime)
        {
            Vector3 vecTractive, vecDrag, vecFriction, vecForce, vecAccel;
            float fSpeed;

            try
            {
                // Scale our traction force by the amount we have available.
                _Force *= _Traction;

                // First calculate the tractive force of the body
                vecTractive = _Force + (_Gravity * _Mass);

                // Now calculate the speed the body is currently moving
                fSpeed = _Velocity.Length();

                // Calculate drag / air resistance (relative to the speed squared).
                vecDrag = -_AirFriction * (_Velocity * fSpeed);

                // Calculate the friction force
                vecFriction = -(_Traction * _Friction) * _Velocity;

                // Calculate our final force vector
                vecForce = vecTractive + vecDrag + vecFriction;

                // Now calculate acceleration
                vecAccel = vecForce / _Mass;

                // Finally apply the acceleration for this frame
                _Velocity += vecAccel * (float)gameTime.ElapsedGameTime.TotalMilliseconds;

                // Now set our connected objects position according to the velocity
                if (!float.IsNaN(fSpeed) || fSpeed > 0)
                {
                    Vector3.Clamp(_Velocity, -new Vector3(_ConnectedObject.MaxVelocity), new Vector3(_ConnectedObject.MaxVelocity));
                    ((Player)_ConnectedObject).Position += _Velocity;
                }
                else
                    _Velocity = Vector3.Zero;

                // Reset our 'motor' force.
                _Force = Vector3.Zero;
            }
            catch
            {
                _Force = Vector3.Zero;
            }
        }
    }
}
